package com.upb.webserver.fisco.contract;

import com.fasterxml.jackson.core.type.TypeReference;
import com.upb.webserver.application.ApplicationManager;
import com.upb.webserver.common.enums.ChainTypeEnum;
import com.upb.webserver.common.enums.PlatformEnum;
import com.upb.webserver.common.exception.OperationException;
import com.upb.webserver.common.exception.WebServerException;
import com.upb.webserver.common.pojo.base.BasePageResponse;
import com.upb.webserver.common.pojo.contract.RspContractVO;
import com.upb.webserver.common.pojo.fisco.contract.ReqSaveContractVO;
import com.upb.webserver.common.pojo.rest.fisco.chainmgr.req.ReqContractSave;
import com.upb.webserver.common.pojo.rest.fisco.chainmgr.resp.RespContract;
import com.upb.webserver.common.pojo.rest.fisco.data.resp.RspContractDeployDTO;
import com.upb.webserver.common.returncode.ConstantCode;
import com.upb.webserver.common.tools.JsonUtils;
import com.upb.webserver.contract.ContractManager;
import com.upb.webserver.dao.entity.TbApplication;
import com.upb.webserver.dao.mapper.TbChainMapper;
import com.upb.webserver.rest.fisco.FiscoChainMgrRestService;
import com.upb.webserver.user.UserManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

@Slf4j
@Service
public class FiscoContractService {
    @Autowired
    private TbChainMapper chainMapper;
    @Autowired
    private ContractManager contractManager;
    @Autowired
    private FiscoChainMgrRestService fiscoChainMgrRestService;
    //    @Autowired
//    private TbContractMapper contractMapper;
    @Autowired
    private ApplicationManager applicationManager;
    @Autowired
    private UserManager userManager;

    /**
     * @param paramVo
     */
//    @Transactional
    public RespContract saveContract(ReqSaveContractVO paramVo) {
        log.info("start exec method [saveContract]. remoteChainId:{} paramVo:{}", JsonUtils.objToString(paramVo));

        //check applicationId
        TbApplication tbApplication = applicationManager.verifyApplicationId(paramVo.getApplicationId());

        //save to chain-manager
        if (ChainTypeEnum.FISCO.getId() != tbApplication.getChainType())
            throw new WebServerException(ConstantCode.CHAIN_TYPE_NOT_SUPPORT.attach(String.format("not support chainType:%s", tbApplication.getChainType())));

        //save to chain-manager
        Integer contractId = saveContractToFiscoChainManager(tbApplication.getChainId(), tbApplication.getId(), paramVo);

        //rsp
        RespContract rspContractVO = new RespContract();
        rspContractVO.setContractId(contractId);

        log.info("success exec method [saveContract].  response:{}", JsonUtils.objToString(rspContractVO));
        return rspContractVO;


//
//
//
//
//        //check chainId
//        TbChain tbChain = chainManager.verifyChainId(tbApplication.getChainId());
//        Integer remoteContractIdOfSaved = null;
//        if (paramVo.getContractId() != null) {
//            TbContract tbContract = contractManager.verifyContractId(paramVo.getContractId());
//            remoteContractIdOfSaved = tbContract.getRemoteId();
//        }
//
//
//        //save to db
//        TbContract contract = contractManager.saveContract(tbChain.getId(), tbApplication.getId(), remoteContractIdOfSaved, paramVo.getContractName(), ChainTypeEnum.FISCO, ContractTypeEnum.SOLIDITY);
//
//        //save to chain-manager
//        if (ChainTypeEnum.FISCO.getId() != tbChain.getChainType()) {
//            log.warn("not support chainType:{}", tbChain.getChainType());
//            throw new WebServerException(ConstantCode.CHAIN_TYPE_NOT_SUPPORT);
//        }
//
//        Integer remoteContractIdOfRestBack = saveContractToFiscoChainManager(tbChain.getId(), tbApplication.getId(), remoteContractIdOfSaved, paramVo);
//
//
//        log.info("remoteContractIdOfSaved:{} remoteContractIdOfRestBack:{}", remoteContractIdOfSaved, remoteContractIdOfRestBack);
//        if (null == remoteContractIdOfSaved && remoteContractIdOfRestBack != null) {
//            log.info("save remote contractId :{}", remoteContractIdOfRestBack);
//            //save remote id
//            contractManager.updateRemoteId(contract.getId(), remoteContractIdOfRestBack);
//        }
//
//        //rsp
//        RespContract rspContractVO = new RespContract();
//        rspContractVO.setChainId(contract.getChainId());
//        rspContractVO.setContractName(contract.getName());
//        rspContractVO.setGroupId(contract.getApplicationId());
//
//
//        BeanUtils.copyProperties(contract, rspContractVO);
//        log.info("success exec method [saveContract]. remoteContractId:{} response:{}", remoteContractIdOfRestBack, JsonUtils.objToString(rspContractVO));
//        return rspContractVO;
    }

    /**
     * @param remoteChainId
     * @param remoteGroupId
     * @param paramVo
     */
//    @Transactional
    public Integer saveContractToFiscoChainManager(Integer remoteChainId, Integer remoteGroupId, ReqSaveContractVO paramVo) {
        log.info("start exec method [saveContractToFiscoChainManager]. remoteChainId:{} remoteGroupId:{} paramVo:{}", remoteChainId, remoteGroupId, JsonUtils.objToString(paramVo));
        ReqContractSave restParam = new ReqContractSave();
        BeanUtils.copyProperties(paramVo, restParam);
        restParam.setChainId(remoteChainId);
        restParam.setGroupId(remoteGroupId);
        RespContract contractRsp = fiscoChainMgrRestService.saveContract(restParam);
        Integer contractId = contractRsp.getContractId();
        log.info("success exec method [saveContractToFiscoChainManager]. contractId:{}", contractId);
        return contractId;
    }


    /**
     * @param platform
     * @param developerId
     * @param contractId
     * @param constructorParams
     */
    public RspContractDeployDTO deployContract(int platform, int developerId, int contractId, String signUserId, List<Object> constructorParams) throws OperationException {
        log.info("start exec method [deployContract]. platform:{} developerId:{}  contractId:{} signUserId:{} constructorParams:{}", platform, developerId, contractId, signUserId, JsonUtils.objToString(constructorParams));

        if (platform != PlatformEnum.DEVELOPER.getId()) {
            log.warn("fail exec method [deployContract]. request from platform:{} ,but support:{}", platform, PlatformEnum.DEVELOPER.getId());
            throw new WebServerException(ConstantCode.PLATFORM_NOT_SUPPORT);
        }

        // TODO 检查当前用户是否有权限操作该

        RspContractDeployDTO deployResult = fiscoChainMgrRestService.deployContract(contractId, signUserId, constructorParams);
        return deployResult;

//        if (platform != PlatformEnum.DEVELOPER.getId()) {
//            log.warn("fail exec method [deployContract]. request from platform:{} ,but support:{}", platform, PlatformEnum.DEVELOPER.getId());
//            throw new WebServerException(ConstantCode.PLATFORM_NOT_SUPPORT);
//        }
//
//        //verify id
//        TbContract contract = contractManager.verifyContractId(contractId);
//
//        //check userid
//        TbUser tbUser = userManager.verifyUserId(userId);
//
//        //deploy
//        RspContractDeployDTO rspDeployDTO = null;
//        contract.setStatus(ContractStatusEnum.DEPLOY_FAIL.getId());
//        try {
//            rspDeployDTO = fiscoChainMgrRestService.deployContract(contract.getRemoteId(), String.valueOf(contract.getApplicationId()), tbUser.getUuid(), constructorParams);
//            log.info("deploy result:{}", JsonUtils.objToString(rspDeployDTO));
//        } catch (WebServerException ex) {
//            log.error("WebServerException.fail exec deploy channel to remote server", ex);
//            contractMapper.updateByPrimaryKey(contract);
//            throw OperationException.fromWebServerException(ex);
//        } catch (Exception ex) {
//            log.warn("fail exec method [deployContract]. contractId:{} developerId:{} userId:{} constructorParams:{}", contractId, developerId, userId, JsonUtils.objToString(constructorParams), ex);
//            contractMapper.updateByPrimaryKey(contract);
//            throw new OperationException(ConstantCode.CHAIN_DEPLOY_FAIL.msg(ex.getMessage()));
//        }
//
//        //update contract status and status
//        if (Objects.nonNull(rspDeployDTO) && StringUtils.isNotBlank(rspDeployDTO.getContractAddress())) {
//            contract.setStatus(ContractStatusEnum.DEPLOY_SUCCESS.getId());
//            contract.setAddress(rspDeployDTO.getContractAddress());
//        }
//        contract.setModifyTime(new Date());
//        contractMapper.updateByPrimaryKey(contract);
//
//        log.info("success exec method [deployContract]. contractId:{}", contractId);
    }


    /**
     * @param chainId
     * @param applicationId
     * @return
     */
    public List<RespContract> queryContractList(Integer chainId, Integer applicationId) {
        log.info("start exec method [queryContractList]. chainId:{} applicationId:{}", chainId, applicationId);
//        List<RespContract> contractVoList = new ArrayList<>();
        List<RespContract> respContractList = fiscoChainMgrRestService.selectContract(chainId, applicationId);
//        log.info("respContractList:{}", JsonUtils.objToString(respContractList));
//        if (CollectionUtil.isEmpty(respContractList)) {
//            return contractVoList;
//        }
//
//        //query by remoteId
//        List<Integer> remoteContractIdList = respContractList.stream().map(rc -> rc.getContractId()).collect(Collectors.toList());
//        TbContractExample example = new TbContractExample();
//        TbContractExample.Criteria criteria = example.createCriteria();
//        criteria.andRemoteIdIn(remoteContractIdList);
//        List<TbContract> currentContractList = contractMapper.selectByExample(example);
//        for (TbContract currentContract : currentContractList) {
//            RespContract respContract = respContractList.stream().filter(rc -> rc.getContractId().equals(currentContract.getRemoteId())).findFirst().get();
//            RespContract contractVo = new RespContract();
//            BeanUtils.copyProperties(respContract, contractVo);
//            contractVo.setContractId(currentContract.getId());
//            contractVoList.add(contractVo);
//        }

        log.info("success exec method [queryContractList]. result:{}", JsonUtils.objToString(respContractList));
        return respContractList;
    }

    public List<RspContractVO> listContractByParam(Map<String, Object> restParam) {
        List<RspContractVO> rspContractVOList = new ArrayList<>();
        BasePageResponse basePageResponse = fiscoChainMgrRestService.pageContractByParam(restParam);
        log.debug("basePageResponse:{}", JsonUtils.objToString(basePageResponse));
        if (basePageResponse.getTotalCount() <= 0)
            return rspContractVOList;
        List<RespContract> contractList = JsonUtils.stringToObj(JsonUtils.objToString(basePageResponse.getData()),
                new TypeReference<List<RespContract>>() {
                });
        contractList.stream()
                .forEach(tbContract -> {
                    RspContractVO rspContractVO = new RspContractVO();
                    BeanUtils.copyProperties(tbContract, rspContractVO);
                    rspContractVO.setChainName(chainMapper.selectNameByPrimaryKey(tbContract.getChainId()));
                    TbApplication tbApplication = applicationManager.verifyApplicationId(tbContract.getGroupId());
                    rspContractVO.setId(tbContract.getContractId());
                    rspContractVO.setApplicationId(tbContract.getGroupId());
                    rspContractVO.setApplicationName(tbApplication.getName());
                    rspContractVO.setApplicationNameZh(tbApplication.getNameZh());
                    rspContractVO.setName(tbContract.getContractName());
                    rspContractVO.setAddress(tbContract.getContractAddress());
                    rspContractVO.setStatus(tbContract.getContractStatus());
                    rspContractVO.setCreateTime(tbContract.getCreateTime());
                    rspContractVOList.add(rspContractVO);
                });
        return rspContractVOList;
    }


    /**
     * @param restParam
     * @return
     */
    public BasePageResponse pageContractByParam(Map<String, Object> restParam) {
        BasePageResponse basePageResponse = new BasePageResponse(ConstantCode.SUCCESS);
        List<RspContractVO> respList = listContractByParam(restParam);
        basePageResponse.setTotalCount(respList.size());
        basePageResponse.setData(respList);
        log.debug("result of pageContractByParam:{}", JsonUtils.objToString(basePageResponse));
        return basePageResponse;
    }
}
